/*
* Copyright (C) 2010-2012 Magnus Skjegstad
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
* 
* The latest version of this library can be downloaded from http://mist-pubsub.googlecode.com
* 
*/
package com.skjegstad.mist2.demos;

import com.skjegstad.mist2.IMistSubscriptionListener;
import com.skjegstad.mist2.MistCoreManager;
import com.skjegstad.mist2.datatypes.DataElement;
import com.skjegstad.mist2.datatypes.NodeIdentifier;
import com.skjegstad.mist2.datatypes.TopicIdentifier;
import com.skjegstad.mist2.exceptions.MistException;
import com.skjegstad.mist2.exceptions.MistHashCollisionException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

/**
 *
 * @author Magnus Skjegstad
 */
public final class TestChat extends javax.swing.JFrame {

    private final MistCoreManager mist = new MistCoreManager();
    private final TopicIdentifier MSG_T = new TopicIdentifier("no.ffi.chat.messages");
    private final TopicIdentifier CMD_T = new TopicIdentifier("no.ffi.chat.commands");
    private final TopicIdentifier TRACKING_T = new TopicIdentifier("no.ffi.tracking.location");
    private final Map<NodeIdentifier, String> nicks = new HashMap();
    private List<NetworkInterface> listeningInterfaces = new ArrayList();
    private String myNick;
    private final Object syncObject = "";
    private final Timer timer = new Timer();
    private final Random r = new Random();
    private final int ptpTcpPort = r.nextInt(65535 - 1025) + 1025;
    private final int ptpUdpPort = r.nextInt(65535 - 1025) + 1025;
    private double autoChatProbability = 0.0;
    private boolean showTrackingData = false;

    /** Creates new form MistChatDemo */
    public TestChat() throws UnknownHostException, MistException, SocketException {

        initComponents();

        jTextField1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jSubmit.doClick();
            }
        });
        jTextField1.setEnabled(true);
        
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                synchronized(syncObject) {
                    try {
                        if (myNick != null && !myNick.isEmpty())
                            sayCommand("nick", new String[]{myNick});
                    } catch (MistHashCollisionException ex) {
                        show(ex.getMessage());
                        ex.printStackTrace(System.err);
                    }
                }
            }
        }, 60 * 1000, 120 * 1000);
        
        timer.scheduleAtFixedRate(new TimerTask() {            
            Random r = new Random();
            @Override
            public void run() {
                if (r.nextDouble() <= autoChatProbability)
                    try {
                        say(LoremIpsum.getRandomLoremIpsum(r.nextInt(30) + 5));
                    } catch (MistHashCollisionException ex) {
                        show(ex.getMessage());
                        ex.printStackTrace(System.err);
                    }
            }
            
        }, 1000, 1000);
                

        jSubmit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String s = jTextField1.getText().trim();

                if (s.isEmpty())
                    return;

                jTextField1.setText("");

                if (s.length() >= 1 && s.charAt(0) == '/') {
                    String[] commandLine = s.split(" ");
                    String command = commandLine[0];

                    if (command.equalsIgnoreCase("/nick")) {
                        if (commandLine.length >= 2) {
                            try {
                                synchronized (syncObject) {
                                    myNick = commandLine[1];
                                    sayCommand("nick", new String[]{myNick});
                                    show("You are now known as " + myNick);
                                }
                            } catch (MistHashCollisionException ex) {
                                show("An error occured while changing nick.");
                                show(ex.getMessage());
                                ex.printStackTrace(System.err);
                            }
                            synchronized (mist) {
                                nicks.put(mist.getMyNodeId(), commandLine[1]);
                            }
                            updateNicks();
                        } else
                            show("Syntax: /nick [nickname]");
                    }  else 
                    if (command.equalsIgnoreCase("/help")) {
                        showHelp();
                    } else
                    if (command.equalsIgnoreCase("/listen")) {
                        if (commandLine.length > 1) {
                            int beaconInterval = 5000;
                            if (commandLine.length > 2) {
                                try {
                                    beaconInterval = Integer.valueOf(commandLine[2]);
                                } catch (NumberFormatException ex) {
                                    show(ex.getMessage());
                                    beaconInterval = -1;
                                }
                            }
                            if (beaconInterval > -1) {
                                show("Attempting to listen on " + commandLine[1] + " with beacon interval " + beaconInterval + " ms");
                                try {
                                    NetworkInterface i = determineNetworkInterface(commandLine[1]);
                                    if (i == null) {
                                        show("Interface " + commandLine[1] + " not found.");
                                    } else
                                    if (listeningInterfaces.contains(i)) {
                                        show("Already listening on interface " + i.getDisplayName());
                                    } else {                                
                                        listen(i, beaconInterval);
                                        listeningInterfaces.add(i);
                                        show("Now listening to interface " + i.toString());
                                    } 
                                } catch (MistException ex) {
                                    show(ex.getMessage());
                                }
                            }
                        } else
                            show("Syntax: /listen [interface or ip]");
                    } else
                    if (command.equalsIgnoreCase("/connect")) {
                      if (commandLine.length > 2) {
                          synchronized(mist){
                                try {
                                    InetAddress ip = InetAddress.getByName(commandLine[1]);
                                    show("Connecting to TCP server " + ip.getHostAddress());
                                    mist.connectToTCPServer(ip, Integer.parseInt(commandLine[2]));                                   
                                } catch (UnknownHostException ex) {
                                    show(ex.getMessage());
                                } catch (MistException ex) {
                                    show(ex.getMessage());
                                }
                          }
                      } else
                          show("Syntax: /connect [ip] [port]");
                    } else
                    if (command.equalsIgnoreCase("/uconnect")) {
                      if (commandLine.length > 2) {
                          synchronized(mist){
                                try {
                                    InetAddress ip = InetAddress.getByName(commandLine[1]);
                                    show("Connecting to UDP unicast neighbor " + ip.getHostAddress());
                                    mist.addUDPUnicastNeighbor(ip, Integer.parseInt(commandLine[2])); 
                                } catch (UnknownHostException ex) {
                                    show(ex.getMessage());
                                } catch (MistException ex) {
                                    show(ex.getMessage());
                                }
                          }
                      } else
                          show("Syntax: /uconnect [ip] [port]");
                    } else
                    if (command.equalsIgnoreCase("/autochat")) {
                        if (commandLine.length > 1) {
                            try {
                                autoChatProbability = Double.valueOf(commandLine[1]).doubleValue();
                                show("Auto chat probability set to " + autoChatProbability);
                            } catch (Exception ex) {
                                show(ex.getMessage());
                            }
                        } else
                            show("Syntax: /autochat [probability]");                        
                    } else
                    if (command.equalsIgnoreCase("/enabletracking")) {
                        showTrackingData = true;
                        show("Location tracking enabled.");
                    } else
                    if (command.equalsIgnoreCase("/disabletracking")) {
                        showTrackingData = false;
                        show("Location tracking disabled");
                    } else
                    if (command.equalsIgnoreCase("/cred")) {
                        if (commandLine.length > 2) {
                            String gwUsername = commandLine[1];
                            String gwPassword = commandLine[2];
                            show("Sending credentials for user " + gwUsername + " to chat gateway(s).");

                            try {
                                sayCommand("cred", new String[] { gwUsername, gwPassword });
                            } catch (MistHashCollisionException ex) {
                                show(ex.getMessage());
                            }
                        } else {
                            show("Syntax: /cred [username] [password]");
                            show("Note! Username and password are sent unencrypted over the network.");
                        }
                    } else
                    if (command.equalsIgnoreCase("/quit")) {
                        timer.cancel();
                        mist.stop();
                        System.exit(0);
                    } else
                    if (command.equalsIgnoreCase("/who")) {
                        who();
                    } else
                        show("Unknown command: " + command);

                    return;
                }
                try {
                    say(s);
                } catch (MistHashCollisionException ex) {
                    ex.printStackTrace(System.err);
                }
                jTextField1.setEnabled(true);
                jTextField1.requestFocus();
            }
        });
        
        synchronized(mist) {                                                     
            mist.subscribe(MSG_T, new IMistSubscriptionListener() {
                public void deliverMessage(DataElement message) {
                    hearMsg(message);
                }
            });
            mist.subscribe(CMD_T, new IMistSubscriptionListener() {
                public void deliverMessage(DataElement message) {
                    hearCommand(message);
                }
            });
            mist.subscribe(TRACKING_T, new IMistSubscriptionListener() {
                public void deliverMessage(DataElement message) {
                    if (showTrackingData) {                        
                        ByteArrayInputStream bis = new ByteArrayInputStream(message.getPayload());
                        DataInputStream dis = new DataInputStream(bis);                        
                        try {
                            String s = "";
                            s = s + "id: " + message.getSourceNode().getNodeIdentifier() + " ";
                            s = s + "t:" + dis.readLong() + " ";
                            s = s + "lat: " + dis.readDouble() + " ";
                            s = s + "lon: " + dis.readDouble() + " ";
                            s = s + "alt: " + dis.readDouble() + " ";
                            s = s + "speed: " + dis.readFloat() + " ";
                            s = s + "bearing: " + dis.readFloat();
                            show("*** Location data received:  " + s);
                        } catch (Exception ex) {
                            show ("Unable to parse location: " + ex.getMessage());
                        }
                    }
                }
            });
            mist.setDebug(true);
            mist.start();
            
            show("Mist (c) Magnus Skjegstad, 2010-2013, http://mist-pubsub.googlecode.com");
            show("");
            show("Mist core started.");
            
            try {
                mist.enableTCPServer(ptpTcpPort);
                show("Listening for TCP point-to-point on port " + ptpTcpPort + " on 0.0.0.0 (all interfaces).");                                                              
            } catch (Exception ex) {
                show("Unable to listen to point-to-point tunnels on port " + ptpTcpPort +".");                
            }
            
            try {
                mist.enableUDPUnicastServer(ptpUdpPort);
                show("Listening for UDP point-to-point on port " + ptpUdpPort + " on 0.0.0.0 (all interfaces).");                                                              
            } catch (Exception ex) {
                show("Unable to listen to point-to-point tunnels on port " + ptpUdpPort +".");                
            }
                                                        
            show("");
            show("Use /listen [interface or ip] to enable broadcast chat.");            
            show("Type /help for help.");
            show("");
            show("You are node " + mist.getMyNodeId().getNodeIdentifier());
        }
        //mist.connectToTCPServer(InetAddress.getByName("81.27.47.160"), 5579);        
        jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);        
        TableColumn col = jTable1.getColumnModel().getColumn(0);
        col.setPreferredWidth(200);
        {
            DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
            renderer.setHorizontalAlignment(JLabel.RIGHT);
            col.setCellRenderer(renderer);
        }
        jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
    }
    
    public void listen(NetworkInterface interf, int beaconInterval) throws MistException {
        if (interf == null)
            return;
        
        InetAddress addr;
        try {
            addr = InetAddress.getByName("224.0.0.1");
        } catch (UnknownHostException ex) {
            show("Unable to use multicast address 224.0.0.1. Not listening to multicast.");
            return;
        }
        int port = 1237;
        synchronized(mist) {
            mist.enableMulticast(addr, port, interf, beaconInterval);            
        }
        show("Listening on "+addr.getHostAddress()+", port "+port+" on interface " + interf.getDisplayName());
    }
    
    public synchronized void show(final String message) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                DefaultTableModel t = (DefaultTableModel) jTable1.getModel();
                t.addRow(new Object[] { "", message });
                scrollToBottom();
            }
        });        
    }
    
    public void showHelp() {
        show("Available commands:");
        show("/help                       Show available commands.");
        show("/nick [nick]                Set nick name for this node.");
        show("                            Note: it may take up to 120 seconds to update");
        show("                            all nodes on nick change.");
        show("/who                        Display list of known nodes.");        
        show("/listen [interface or ip] {beacon interval}   Listen to messages on the specified interface. Beacon interval is optional. Default is 5000 ms.");
        show("/connect [ip] [port]        Open point-to-point tunnel to a node with the");
        show("                            specified IP address using TCP.");
        show("/uconnect [ip] [port]       Open point-to-point tunnel to a node with the");
        show("                            specified IP address using UDP.");       
        show("/quit                       Exit application.");
        show("");
        show("Additional commands used in experiments:");
        show("/autochat [probability]     Start automatic chat with a specified probability of sending a message every second.");
        show("/enabletracking             Enable location tracking in mobile nodes.");
        show("/disabletracking            Disable location tracking in mobile nodes.");
        show("/cred [username] [password] Send (unencrypted) username and password to a Mist/Chat-server gateway. ");
        show("");
        show("Available network interfaces:");
        try {
            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
            while (e.hasMoreElements()) {
                NetworkInterface n = e.nextElement();
                String ips = "";
                for (InterfaceAddress ia : n.getInterfaceAddresses()) {
                    if (!ips.equals(""))
                        ips += ", ";
                    ips += ia.getAddress().getHostAddress();
                    if (ia.getAddress() instanceof Inet6Address)
                        ips += " (IPv6)";
                }
                String listening = "";
                if (listeningInterfaces.contains(n))
                    listening = " - You are listening on this interface.";
                show("\t" + n.getDisplayName() + " (" + ips + ") " + listening);
            }
        } catch (SocketException ex) {
            show("Unable to enumerate network interfaces. Got socket exception.");
            ex.printStackTrace(System.err);
        }
        show("");
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jFrame1 = new javax.swing.JFrame();
        jTextField1 = new javax.swing.JTextField();
        jSubmit = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();

        org.jdesktop.layout.GroupLayout jFrame1Layout = new org.jdesktop.layout.GroupLayout(jFrame1.getContentPane());
        jFrame1.getContentPane().setLayout(jFrame1Layout);
        jFrame1Layout.setHorizontalGroup(
            jFrame1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 400, Short.MAX_VALUE)
        );
        jFrame1Layout.setVerticalGroup(
            jFrame1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 300, Short.MAX_VALUE)
        );

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jSubmit.setText("Submit");

        jTable1.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Node", "Message"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        jTable1.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
        jTable1.setEnabled(false);
        jTable1.setFocusable(false);
        jTable1.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                tableResized(evt);
            }
        });
        jScrollPane1.setViewportView(jTable1);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 766, Short.MAX_VALUE)
                    .add(layout.createSequentialGroup()
                        .add(jTextField1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 669, Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jSubmit)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 437, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jSubmit)
                    .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void tableResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tableResized
        TableColumn col1 = jTable1.getColumnModel().getColumn(0);
        TableColumn col2 = jTable1.getColumnModel().getColumn(1);
        
        col2.setPreferredWidth(jTable1.getWidth() - col1.getPreferredWidth() - 3);
    }//GEN-LAST:event_tableResized

    public void sayCommand(String command, String[] params) throws MistHashCollisionException {
        String s = command;
        for (String p : params)
            s = s + ";" + p;        
        synchronized(mist) {
            mist.publish(CMD_T, s.getBytes(), 120000);
        }
    }

    public synchronized void hearCommand(DataElement element) {
        System.out.println("in cmd");

        String s = new String(element.getPayload());
        String[] line = s.split(";");

        if (line.length < 2)
            return;

        String command = line[0];
        String[] params = Arrays.copyOfRange(line, 1, line.length);

        System.out.println("got cmd: " + command + " " + params[0]);

        if (command.equalsIgnoreCase("nick")) {
            String oldNick = nicks.put(element.getSourceNode(), params[0]);
            if (element.getSourceNode().getNodeIdentifier() != mist.getMyNodeId().getNodeIdentifier()) {
                if (oldNick == null)
                    show(element.getSourceNode().getNodeIdentifier() + " is now known as " + params[0]);
                else
                if (!oldNick.equals(params[0]))
                    show(oldNick + "(" + element.getSourceNode().getNodeIdentifier() + ") is now known as " + params[0]);
            }
                
            updateNicks();
        };
    }

    public void say(String msg) throws MistHashCollisionException {
        synchronized (mist) {
            mist.publish(MSG_T, msg.getBytes(), 120000);
        }
    }
    
    public synchronized void scrollToBottom() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                synchronized(jTable1) {
                    jTable1.scrollRectToVisible(jTable1.getCellRect(jTable1.getRowCount()-1, 1, true));
                }
            }
        });
        
    }

    public synchronized void hearMsg(DataElement element) {
        DefaultTableModel t = (DefaultTableModel) jTable1.getModel();
        String msg = new String(element.getPayload());
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm,ss");
        t.addRow(new Object[] { sdf.format(Calendar.getInstance().getTime()) + " "+ getNick(element.getSourceNode()) + " ", msg });
        scrollToBottom();
        
        System.out.println("got msg: " + msg);
    }
    
    public void who() {
        show("Known nodes:");
        Map<NodeIdentifier, String> n = new HashMap(nicks);
        for (Entry<NodeIdentifier, String> e : n.entrySet()) {
            show(e.getValue() + " (" + e.getKey().getNodeIdentifier() + ")");
        }
    }
    
    public String getNick(NodeIdentifier n) {
        String s = nicks.get(n);
        if (s == null)
            s = n.getNodeIdentifier().toString();
        return s;
    }

    public void updateNicks() {
        DefaultTableModel m = (DefaultTableModel) jTable1.getModel();
        for (int i = 0; i < m.getRowCount(); i++) {
            Long l = null;
            try {
                l = Long.decode(((String) m.getValueAt(i, 0)).substring(1));
            } catch (Exception ex) {
                continue;
            }
            String s = nicks.get(new NodeIdentifier(l));
            if (s == null)
                continue;

            m.setValueAt(s, i, 0);
        }
    }

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) throws UnknownHostException, MistException, SocketException {

        final TestChat d = new TestChat();

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                d.setVisible(true);
                d.jTextField1.setEnabled(true);
                d.jTextField1.requestFocus();
            }
        });
    }
    
    public NetworkInterface determineNetworkInterface(String ni) {
        NetworkInterface res = null;
        InetAddress ipaddr = null;     
                
        // try by ip first
        try {
            ipaddr = InetAddress.getByName(ni); 
        } catch (UnknownHostException ex) {
            
        }   
        
        if (ipaddr != null) {
            try {
                Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
                while (e.hasMoreElements() && res == null) {
                    NetworkInterface n = e.nextElement();
                    for (InterfaceAddress ia : n.getInterfaceAddresses()) {
                        if (ia.getAddress().equals(ipaddr)) {
                            System.out.println("# INFO: IP " + ni + " determined to match interface " + n.getDisplayName());
                            return n;
                        }
                    }
                }
            } catch (SocketException ex) {
                show("# FATAL: Unable to enumerate network interfaces. Got socket exception.");
                ex.printStackTrace();
                return null;
            }
        }        

        // try network interface instead
        try {
            res = NetworkInterface.getByName(ni);
            return res;
        } catch (SocketException ex) {
            ex.printStackTrace(System.err);
            res = null;
        }                                  

        show("# FATAL: Unable to determine network interface. Please provide correct interface name or IP.");

        // if we get here, all failed
        return null;

    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JFrame jFrame1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton jSubmit;
    private javax.swing.JTable jTable1;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration//GEN-END:variables

}
